// ==UserScript==
// @name         WoD管理员专用脚本
// @namespace    https://gitee.com/christophero/wod-scripts
// @description  WoD管理员专用脚本
// @author       Christophero
// @match        http*://zhao.world-of-dungeons.org/*
// @icon         http://info.world-of-dungeons.org/wod/css/WOD.gif
// @require      https://code.jquery.com/jquery-3.3.1.min.js
// @grant        none
// @version      2023.08.27.4
// @updateURL    https://gitee.com/christophero/wod-scripts/raw/master/wod/standard/WoDManagerHelper.user.js
// @downloadURL  https://gitee.com/christophero/wod-scripts/raw/master/wod/standard/WoDManagerHelper.user.js
// ==/UserScript==

const WOD_MANAGER_HELPER_CONFIG = "WOD_MANAGER_HELPER_CONFIG";
let funArr = [];
let $panelBody = null;

let globalHeroLv = 0;
let globalAttrVal = 0;
let globalDelHeroCnt = 0;
let globalNeedDelCnt = 0;
let globalSteps = 0;
let globalCurSteps = 0;
let globalCurSteps2 = 0;
const attrEnMap = {
  力量: "st",
  体质: "ko",
  智力: "in",
  灵巧: "ge",
  魅力: "ch",
  敏捷: "sn",
  感知: "wa",
  意志: "wi",
};

const baseParams = [
  "session_hero_id",
  "wod_post_id",
  "wod_post_world",
  "klasse_id",
  "klasse_name",
  "rasse_id",
  "rasse_name",
  "gruppe_id",
  "gruppe_name",
  "clan_id",
  "clan_name",
  "stufe",
  "heldenname",
  "spielername",
  "DTVER",
];

(function () {
  "use strict";
  // 相关模块，注释掉来取消使用
  addControlPanel();
  funControl("easyAddItem", "便捷添加物品", easyAddItem, true);
  funControl("fastLvManage", "快捷等级管理", fastLvManage, true);
  funControl("batchDelHeroes", "批量删除英雄", batchDelHeroes, true);
  funControl("easyChangeSkill", "简易技能等级", easyChangeSkill, true);
  funControl("importHeroTemplate", "导入英雄模版", importHeroTemplate, true);
  funControl("reverseRacePro", "反选种族职业", reverseRacePro, true);
  funControl("easyFetchItems", "快速获取物品", easyFetchItems, true);
})();

function addControlPanel() {
  const $main = $(
    `
    <br>
    <div class="spoilerbox">
      <input type="button" class="spoilerbutton" value="+" onclick="this.value=this.value=='+'?'-':'+';">
      <span class="spoilerheader"><b>管理员控制面板</b></span>
      <div class="spoiler">
        <div>
          <button id="saveManagerConfig" class="button clickable" title="仅启用勾选功能">仅启用勾选功能</button><br>
        </div>
      </div>
    </div>`
  );
  $("#gadgettable-left").append($main);
  try {
    $panelBody = $main.find(".spoiler>div");
  } catch (error) {
    console.log(error);
  }

  $("#saveManagerConfig").click(function () {
    if (confirm("是否仅启用当前勾选功能？")) {
      const config = loadConfig();
      $panelBody.find("input:checkbox").each((i, e) => {
        config[$(e).attr("name")] = $(e).is(":checked");
      });
      saveConfig(config);
      alert("保存完毕");
    }
  });
}

function funControl(name, label, fun, defStatus) {
  let config = loadConfig();
  if (!config.hasOwnProperty(name)) {
    config[name] = defStatus;
    saveConfig(config);
  }
  const $chk = $(`<input type="checkbox" name="${name}">`);
  $chk.prop("checked", config[name]);
  $panelBody.append($chk).append(label).append("<br>");
  if (config[name]) {
    fun();
  } else {
    console.log(label + " 功能未启用");
  }
}

function loadConfig() {
  let configJson = localStorage.getItem(WOD_MANAGER_HELPER_CONFIG);
  try {
    return JSON.parse(configJson) || {};
  } catch (ex) {
    return {};
  }
}

/**
 * 保存配置
 * @param {*} config
 */
function saveConfig(config) {
  localStorage.setItem(WOD_MANAGER_HELPER_CONFIG, JSON.stringify(config));
}

/**
 * 获得基础参数
 * @returns
 */
function getBaseSearchParams() {
  const searchParams = new URLSearchParams();
  $("input:hidden").each(function () {
    const $this = $(this);
    const key = $this.attr("name");
    if (baseParams.includes(key)) {
      searchParams.set($this.attr("name"), $this.val());
    }
  });
  return searchParams;
}

/**
 * @function 便捷添加物品模块
 */
function easyAddItem() {
  const urls = [
    "/wod/spiel/admin/heroitems.php",
    "/wod/module/search/search_item.php",
  ];
  if (!urls.includes(location.pathname)) {
    return;
  }
  // 物品添加到仓库不再刷新页面
  $('input[name="si_action_exec"]')
    .attr("type", "button")
    .click(function () {
      const $form = $(
        '.main_content form[action^="/wod/module/search/search_item.php"]'
      );
      const url = $form.attr("action");
      const params = $form.serialize();
      const searchParams = new URLSearchParams(params);
      searchParams.set("si_action_exec", "添加");
      ajaxAlert("处理中，请稍等...");
      fetch(location.origin + url, {
        headers: {
          accept:
            "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
          "content-type": "application/x-www-form-urlencoded",
        },
        method: "POST",
        body: searchParams.toString(),
      }).then((resp) => {
        ajaxAlert("处理完成！");
        setTimeout(function () {
          _ajaxCloseModalDialog("ajax_dialog");
        }, 800);
      });
    });

  // 直接在搜索框回车搜索物品
  $('input[name="edit_gegenstand_vorlage_0name"]').attr(
    "onkeydown",
    "if (event.keyCode == 13) {this.nextElementSibling.click();return false;}"
  );
}

/**
 * 快速等级管理
 */
function fastLvManage() {
  const urls = ["/wod/spiel/hero/attributes.php"];
  if (!urls.includes(location.pathname)) {
    return;
  }
  const urlParams = getBaseSearchParams();
  globalCurSteps = parseInt(urlParams.get("stufe"));
  // 可以升级就添加一个一键冲顶
  if (globalCurSteps < 40) {
    $('<input type="button" value="直升40" class="button clickable">')
      .appendTo($('td:contains("英雄等级")').siblings("td:last").find("i"))
      .click(async function () {
        if (confirm("是否预先获取大量升级资源")) {
          ajaxAlert("正在获得升级所需资源...");
          await gainResource(urlParams);
        }
        const diffLv = 40 - globalCurSteps;
        globalSteps = 40;
        const promiseArr = [];
        for (let i = 0; i < diffLv; i++) {
          promiseArr.push(lvUp(urlParams));
        }
        Promise.all(promiseArr).then((responseArr) => {
          ajaxAlert("处理完成，即将刷新页面");
          setTimeout(function () {
            _ajaxCloseModalDialog("ajax_dialog");
            location.replace(location.href);
          }, 800);
        });
      });
  }

  $('<input type="button" value="自定等级" class="button clickable">')
    .appendTo($('td:contains("英雄等级")').siblings("td:last").find("i"))
    .click(async function () {
      let targetVal = prompt("请输入目标等级", 40);
      if (targetVal == null) return;
      targetVal = parseNum(targetVal);
      if (targetVal > 40 || targetVal < 1) {
        alert("目标等级只能在1-40之间！");
        return;
      }
      const diffLv = Math.abs(targetVal - globalCurSteps);
      globalSteps = targetVal;
      const isLvUp = globalCurSteps < targetVal;
      if (isLvUp && confirm("是否预先获取大量升级资源")) {
        ajaxAlert("正在获得升级所需资源...");
        await gainResource(urlParams);
      }
      const promiseArr = [];
      for (let i = 0; i < diffLv; i++) {
        promiseArr.push(isLvUp ? lvUp(urlParams) : lvDown(urlParams));
      }
      Promise.all(promiseArr).then((responseArr) => {
        ajaxAlert("处理完成，即将刷新页面");
        setTimeout(function () {
          _ajaxCloseModalDialog("ajax_dialog");
          location.replace(location.href);
        }, 800);
      });
    });

  // 添加便捷属性
  $(
    '<img src="/wod/css/skins/skin-7/images/icons/reset.gif" style="cursor:pointer;width:20px;height:20px;"/>'
  )
    .insertAfter(
      $('input[name^="improve[at_"], img[src$="icons/steigern_disabled.gif"]')
    )
    .click(async function () {
      const urlParams = getBaseSearchParams();
      let targetVal = prompt("请输入目标数值", 10);
      if (targetVal == null) return;
      targetVal = parseNum(targetVal);
      if (targetVal > 30) {
        alert("目标数值不能大于30！");
        return;
      }
      const attrName = $(this)
        .parents("tr:eq(1)")
        .find("td:first")
        .text()
        .trim();
      ajaxAlert("正在处理，请稍候...");
      await attrChangeBatch(urlParams, attrName, targetVal);
      ajaxAlert("处理完成，即将刷新页面");
      setTimeout(function () {
        _ajaxCloseModalDialog("ajax_dialog");
        location.replace(location.origin + "/wod/spiel/hero/attributes.php");
      }, 800);
    });

  // 添加高手低手全13配置
  const $attrTableTd = $(
    '#main_content h1:contains("属性与特性") +table:first td:first'
  );
  $('<input type="button" value="成为高手" class="button clickable">')
    .appendTo($attrTableTd)
    .click(async function () {
      ajaxAlert("正在处理，请稍候...");
      await allAttrTo(urlParams, 2);
      ajaxAlert("处理完成，即将刷新页面");
      setTimeout(function () {
        _ajaxCloseModalDialog("ajax_dialog");
        location.replace(location.origin + "/wod/spiel/hero/attributes.php");
      }, 800);
    });

  $('<input type="button" value="变成低手" class="button clickable">')
    .appendTo($attrTableTd)
    .click(async function () {
      ajaxAlert("正在处理，请稍候...");
      await allAttrTo(urlParams, 10);
      ajaxAlert("处理完成，即将刷新页面");
      setTimeout(function () {
        _ajaxCloseModalDialog("ajax_dialog");
        location.replace(location.origin + "/wod/spiel/hero/attributes.php");
      }, 800);
    });

  $('<input type="button" value="八徽章" class="button clickable">')
    .appendTo($attrTableTd)
    .click(async function () {
      ajaxAlert("正在处理，请稍候...");
      await allAttrTo(urlParams, 13);
      ajaxAlert("处理完成，即将刷新页面");
      setTimeout(function () {
        _ajaxCloseModalDialog("ajax_dialog");
        location.replace(location.origin + "/wod/spiel/hero/attributes.php");
      }, 800);
    });
}

/**
 * 获取资源
 * @param {*} urlParams
 * @returns
 */
function gainResource(urlParams) {
  urlParams.set("id", urlParams.get("session_hero_id"));
  urlParams.set("add_fame", "10000000");
  urlParams.set("perfame", "");
  urlParams.set("add_gold", "20000000");
  urlParams.set("pergold", "");
  urlParams.set("add_ep", "1596550");
  urlParams.set("perep", "");
  urlParams.set("add_rp", "100");
  urlParams.set("add_elo", "");
  urlParams.set("exec", "  √  ");

  const url = `${
    location.origin
  }/wod/spiel/hero/admin.php?is_popup=1&session_hero_id=${urlParams.get(
    "session_hero_id"
  )}`;
  return fetch(url, {
    headers: {
      accept:
        "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
      "content-type": "application/x-www-form-urlencoded",
    },
    body: urlParams.toString(),
    method: "POST",
  }).then((response) => {
    ajaxAlert(`已获得40级所需经验，1000W荣誉，2000万金钱，100重置点`);
    return response.text();
  });
}

/**
 * 等级提升1级
 * @param {*} urlParams
 * @returns
 */
async function lvUp(urlParams) {
  urlParams.set("do_reset", "");
  urlParams.set("levelup_warned", "0");
  urlParams.set("manualLevelUp", "升级");
  urlParams.set("geschlecht", "f");
  urlParams.set("amor_details", "关闭");
  const url = `${
    location.origin
  }/wod/spiel/hero/attributes.php?is_popup=1&session_hero_id=${urlParams.get(
    "session_hero_id"
  )}`;
  const response = await fetch(url, {
    headers: {
      accept:
        "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
      "content-type": "application/x-www-form-urlencoded",
    },
    body: urlParams.toString(),
    method: "POST",
  });
  globalCurSteps++;
  ajaxAlert(`升级进度： ${globalCurSteps}/${globalSteps}`);
  return response.text();
}

async function lvDown(urlParams) {
  const url = `${
    location.origin
  }/wod/spiel/hero/admin.php?is_popup=1&session_hero_id=${urlParams.get(
    "session_hero_id"
  )}`;
  urlParams.set("id", urlParams.get("session_hero_id"));
  urlParams.set("add_fame", "");
  urlParams.set("perfame", "");
  urlParams.set("add_gold", "");
  urlParams.set("pergold", "");
  urlParams.set("add_ep", "");
  urlParams.set("perep", "");
  urlParams.set("add_rp", "");
  urlParams.set("add_elo", "");
  urlParams.set("reduce_level", "降低英雄等级");
  const response = await fetch(url, {
    headers: {
      accept:
        "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
      "content-type": "application/x-www-form-urlencoded",
    },
    body: urlParams.toString(),
    method: "POST",
  });
  globalCurSteps--;
  ajaxAlert(`降级进度： ${globalCurSteps}/${globalSteps}`);
  return response.text();
}

/**
 * 获得角色属性数值
 * @param {*} attr
 * @returns
 */
function getAttrVal(attr, $context) {
  $context = $context || $(document);
  let val = $context
    .find(`td:contains("${attr}")`)
    .next()
    .find("td:eq(1)")[0]
    .childNodes[0].textContent.trim();
  val = parseInt(val);
  return val;
}

function parseNum(numStr) {
  let num = 1;
  try {
    num = parseInt(numStr);
  } catch (ex) {
    num = 1;
  }
  if (isNaN(num) || num < 0) {
    num = 1;
  }
  return num;
}

/**
 * 所有属性改变到指定值
 * @param {*} urlParams
 * @param {*} targetVal
 */
async function allAttrTo(urlParams, targetVal) {
  await attrChangeBatch(urlParams, "力量", targetVal);
  await attrChangeBatch(urlParams, "体质", targetVal);
  await attrChangeBatch(urlParams, "智力", targetVal);
  await attrChangeBatch(urlParams, "灵巧", targetVal);
  await attrChangeBatch(urlParams, "魅力", targetVal);
  await attrChangeBatch(urlParams, "敏捷", targetVal);
  await attrChangeBatch(urlParams, "感知", targetVal);
  await attrChangeBatch(urlParams, "意志", targetVal);
}

/**
 * 属性变更
 * @param {*} urlParams
 * @param {*} attrName
 * @param {*} improve
 * @returns
 */
async function attrChange(urlParams, attrName, improve) {
  const pname = `${improve ? "improve" : "undo"}[at_${attrEnMap[attrName]}]`;
  const params = new URLSearchParams(urlParams.toString());
  params.set(pname + ".x", 10);
  params.set(pname + ".y", 10);
  params.set("do_reset", "");
  params.set("levelup_warned", "0");
  params.set("geschlecht", "f");
  params.set("amor_details", "关闭");
  const url = `${
    location.origin
  }/wod/spiel/hero/attributes.php?is_popup=1&session_hero_id=${params.get(
    "session_hero_id"
  )}`;
  const response = await fetch(url, {
    headers: {
      accept:
        "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
      "content-type": "application/x-www-form-urlencoded",
    },
    body: params.toString(),
    method: "POST",
  });
  globalCurSteps++;
  ajaxAlert(`${attrName}变更进度： ${globalCurSteps}/${globalSteps}`);
  return await response.text();
}

/**
 * 属性批量变更
 * @param {*} urlParams
 * @param {*} attrName
 * @param {*} targetVal
 */
async function attrChangeBatch(urlParams, attrName, targetVal) {
  const curVal = getAttrVal(attrName);
  await attrChangeBatchWithInitial(urlParams, attrName, curVal, targetVal);
}

/**
 * 属性批量变更(手动指定初始属性)
 * @param {*} urlParams
 * @param {*} attrName
 * @param {*} curVal
 * @param {*} targetVal
 */
async function attrChangeBatchWithInitial(
  urlParams,
  attrName,
  curVal,
  targetVal
) {
  globalCurSteps = 0;
  globalSteps = Math.abs(targetVal - curVal);
  let improve = true;
  if (curVal > targetVal) {
    improve = false;
  }
  for (let i = 0; i < globalSteps; i++) {
    await attrChange(urlParams, attrName, improve);
  }
}

/**
 * 批量删除勾选的英雄
 */
function batchDelHeroes() {
  const urls = ["/wod/spiel/settings/heroes.php"];
  if (!urls.includes(location.pathname)) {
    return;
  }
  // 添加批量删除的按钮
  const $batchDelBtn = $(
    '<input type="button" name="BATCH_DEL" value="批量删除" class="button clickable">'
  );
  $batchDelBtn.insertAfter('input[name="LOESCHEN"]').click(function () {
    globalDelHeroCnt = 0;
    globalNeedDelCnt = 0;
    const delList = [];
    // 获取勾选的英雄
    $('input[name^="aktiv"]:checked').each((i, e) => {
      const $row = $(e).parents("tr:first");
      let heroName = $row.find("td:first a").text();
      let heroId = $row.find('td:first input[type="radio"]').val();
      delList.push({ heroId, heroName });
    });
    if (
      confirm(
        `是否删除下列英雄？\n\n${delList.map((h) => h.heroName).join("\n")}`
      )
    ) {
      globalNeedDelCnt = delList.length;
      ajaxAlert(`删除进度： ${globalDelHeroCnt}/${globalNeedDelCnt}`);
      const promiseArr = [];
      delList.forEach((h) => promiseArr.push(delHero(h.heroId, h.heroName)));
      Promise.all(promiseArr).then((responseArr) => {
        ajaxAlert("处理完成，即将刷新页面");
        setTimeout(function () {
          _ajaxCloseModalDialog("ajax_dialog");
          location.replace(location.href);
        }, 800);
      });
    }
  });
}

/**
 * 删除英雄
 * @param {*} heroId
 * @param {*} heroName
 * @returns
 */
function delHero(heroId, heroName) {
  const urlParams = getBaseSearchParams();
  urlParams.set("CONFIRM", "yes");
  urlParams.set("FIGUR", heroId);
  urlParams.set("LOESCHEN_BEST", "%E5%88%A0%E9%99%A4");
  const url = `${
    location.origin
  }/wod/spiel/settings/heroes.php?is_popup=1&session_hero_id=${urlParams.get(
    "session_hero_id"
  )}`;
  return fetch(url, {
    headers: {
      accept:
        "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
      "content-type": "application/x-www-form-urlencoded",
    },
    body: urlParams.toString(),
    method: "POST",
  }).then((response) => {
    globalDelHeroCnt++;
    ajaxAlert(`删除进度： ${globalDelHeroCnt}/${globalNeedDelCnt}`);
    return response.text();
  });
}

function easyChangeSkill() {
  const urls = ["/wod/spiel/hero/skills.php"];
  if (!urls.includes(location.pathname)) {
    return;
  }
  const urlParams = getBaseSearchParams();
  $(
    '<img src="/wod/css/skins/skin-7/images/icons/reset.gif" style="cursor:pointer;width:20px;height:20px;"/>'
  )
    .insertAfter(
      $(
        'input[id^="button_steigern_"], img[src$="icons/steigern_disabled.gif"]'
      )
    )
    .click(async function () {
      const urlParams = getBaseSearchParams();
      let btnId = $(this).prev('input[id^="button_steigern_"]').attr("id");
      const skillId = /button_steigern_(\d+)/.exec(btnId)[1];
      const skillLv =
        parseInt(
          $(this).parents("tr:first").find("div[id^=skill_rang]").text().trim()
        ) || 0;
      let targetVal = prompt("请输入目标技能等级", skillLv);
      if (targetVal == null) return;
      targetVal = parseNum(targetVal);
      changeSkillBatch(skillId, skillLv, targetVal, true);
    });
}

/**
 * 快速变更技能等级
 * @param {*} skillId
 * @param {*} curLv
 * @param {*} targetLv
 * @param {*} force
 * @returns
 */
function changeSkillBatch(skillId, curLv, targetLv, force) {
  if (targetLv > 50 || targetLv < 0) {
    alert("目标技能等级只能在0到50之间！");
    return;
  }
  const diff = Math.abs(targetLv - curLv);
  for (let i = 0; i < diff; i++) {
    change_skill(skillId, curLv > targetLv ? "-" : "+", force);
  }
}

// 导入英雄模版
function importHeroTemplate() {
  // 只在技能页面生效
  const urls = ["/wod/spiel/hero/skills.php"];
  if (!urls.includes(location.pathname)) {
    return;
  }
  // 技能列表页面添加导入英雄模版按钮
  const $btn = $(
    '<button class="button clickable" type="button">导入英雄模版</button>'
  )
    .insertBefore('input[name="hide_all"]')
    .click(async function () {
      const urlParams = getBaseSearchParams();
      // 1. 获取人物卡
      // 2. 分析人物卡
      // 3. 获取8属性数值
      // 4. 比较属性差异，升级或者降级属性
      // 5. 比较技能差异，升级或者降级技能

      createInputHeroCardDialog(urlParams, analyseHeroCard);
    });
}

function createInputHeroCardDialog(urlParams, callback) {
  const targetId = "ajax_editor";
  const innerHtml =
    '<div id="heroCardContainer" contentEditable="true" style="height: 100%;"></div>';
  _ajaxCreateDialogDirect(
    targetId,
    innerHtml,
    "请在下方输入人物卡，不要有多余文本",
    AJAX_YES | AJAX_CLOSE,
    async function (event) {
      if (event == "yes") {
        let heroCard = document.getElementById("heroCardContainer").textContent;
        callback(urlParams, heroCard);
      } else {
        _ajaxStopWaiting(targetId);
        _ajaxCloseModalDialog("ajax_editor");
      }
    },
    function () {
      console.log("init");
      document.querySelector("#ajax_editor_buttons button").innerText = "导入";
    }
  );
}

async function analyseHeroCard(urlParams, heroCard) {
  try {
    heroCard = heroCard
      .replace(/\[\d+\]/g, "")
      .replace(/:g\w:/g, "")
      .replace(/\[test\]/g, "")
      .replace(/\[clone\]/g, "")
      .replace(/\[/g, "<")
      .replace(/\]/g, ">")
      .replace(/[\r\n]/g, "")
      .replace(
        /<skill:\s*(.+?)\s*>/g,
        (fullHtml, skillName) => `<skill>${skillName}</skill>`
      )
      .replace(
        /<item:\s*(.+?)\s*>/g,
        (fullHtml, itemName) => `<item>${itemName}</item>`
      )
      .replace(/color=orange/g, "orange");
  } catch (error) {
    ajaxAlert("浏览器版本过低，不能使用replaceAll函数");
    return;
  }
  let $heroCard;
  try {
    $heroCard = $(heroCard);
  } catch (error) {
    ajaxAlert("人物卡格式可能存在问题，不能正确分析");
    return;
  }
  const attrNameList = Object.keys(attrEnMap);
  ajaxAlert("1. 分析人物卡属性清单");
  // 获取属性清单
  const targetAttrMap = {};
  $heroCard
    .find(attrNameList.map((attr) => `orange:contains("${attr}")`).join(","))
    .each(function (i, e) {
      const attrVal = $(e).parent().next().text();
      const attrName = e.textContent;
      targetAttrMap[attrName] = parseInt(attrVal);
    });
  ajaxAlert("2. 分析人物卡技能清单");
  // 获取技能等级一览
  const skillLvMap = {};
  $heroCard.find("skill").each(function (i, e) {
    const skillLv = $(e).parent().next().text();
    const skillName = e.textContent;
    skillLvMap[skillName] = parseInt(skillLv);
  });
  ajaxAlert("3. 分析人物卡装备清单");
  // 获取装备物品一览
  const equipItems = [];
  $heroCard.find("item").each((i, e) => equipItems.push(e.textContent));
  console.log(targetAttrMap);
  console.log(skillLvMap);
  console.log(equipItems);
  const currentAttrMap = await getHeroAttrs(urlParams);
  ajaxAlert("4. 开始进行属性变更");
  // 变更属性
  for (let attrName of attrNameList) {
    await attrChangeBatchWithInitial(
      urlParams,
      attrName,
      currentAttrMap[attrName],
      targetAttrMap[attrName]
    );
  }
  ajaxAlert("5. 开始变更当前技能等级");
  // 变更页面上的技能等级
  for (let skillName of Object.keys(skillLvMap)) {
    const $skillAnchor = $(
      `a[href^="/wod/spiel/hero/skill.php"]:contains("${skillName}")`
    );
    if (!$skillAnchor.length) continue;
    const $skillRang = $skillAnchor
      .parent("td:first")
      .next()
      .find('[id^="skill_rang_"]');
    if (!$skillRang.length) continue;
    const skillId = $skillRang.attr("id").replace("skill_rang_", "");
    const skillLv = parseInt($skillRang.text().trim()) || 0;
    changeSkillBatch(skillId, skillLv, skillLvMap[skillName], true);
  }
  ajaxAlert("6. 开始获取装备");

  if (confirm("是否获取人物卡相关装备")) {
    globalSteps = equipItems.length;
    globalCurSteps = 0;
    globalCurSteps2 = 0;
    showItemFetchProcess();
    const promiseArr = [];
    for (let itemName of equipItems) {
      promiseArr.push(getItemByName(urlParams, itemName, "4.00"));
    }
    const responseArr = await Promise.all(promiseArr);
  }
  ajaxAlert("处理完毕！");
  setTimeout(function () {
    _ajaxStopWaiting("ajax_editor");
  }, 800);
}

async function getHeroAttrs(urlParams) {
  ajaxAlert("正在获取英雄当前属性");
  const shId = urlParams.get("session_hero_id");
  const url =
    location.origin +
    "/wod/spiel/hero/attributes.php?is_popup=1&session_hero_id=" +
    shId;
  const response = await fetch(url, {
    headers: {
      accept:
        "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
      "content-type": "application/x-www-form-urlencoded",
    },
    body: urlParams.toString(),
    method: "POST",
  });
  const text = await response.text();
  const $context = $(text);
  const attrMap = {
    力量: getAttrVal("力量", $context),
    体质: getAttrVal("体质", $context),
    智力: getAttrVal("智力", $context),
    灵巧: getAttrVal("灵巧", $context),
    魅力: getAttrVal("魅力", $context),
    敏捷: getAttrVal("敏捷", $context),
    感知: getAttrVal("感知", $context),
    意志: getAttrVal("意志", $context),
  };
  console.log(attrMap);
  ajaxAlert(
    `英雄当前属性已获取：<br>
    力量：${attrMap["力量"]}<br>
    体质：${attrMap["体质"]}<br>
    智力：${attrMap["智力"]}<br>
    灵巧：${attrMap["灵巧"]}<br>
    魅力：${attrMap["魅力"]}<br>
    敏捷：${attrMap["敏捷"]}<br>
    感知：${attrMap["感知"]}<br>
    意志：${attrMap["意志"]}`
  );
  return attrMap;
}

async function searchItemIdByNameAndVer(params, itemName, ver) {
  const urlParams = new URLSearchParams(params.toString());
  urlParams.set("edit_gegenstand_vorlage_0name", itemName);
  urlParams.set("search_options_which_vers", ver);
  urlParams.set("edit_gegenstand_vorlage_0gegenstand_klasse", "0");
  urlParams.set("edit_gegenstand_vorlage_0trage_typ", "");
  urlParams.set("edit_gegenstand_vorlage_0drop_klasse", "0");
  urlParams.set("edit_gegenstand_vorlage_0unique_type", "");
  urlParams.set("edit_gegenstand_vorlage_0drop_from", "");
  urlParams.set("edit_gegenstand_vorlage_0drop_to", "");
  urlParams.set("edit_gegenstand_vorlage_0set", "0");
  urlParams.set("edit_gegenstand_vorlage_0drop_haeufigkeit", "-1");
  urlParams.set("edit_gegenstand_vorlage_0usage_items", "");
  urlParams.set("edit_gegenstand_vorlage_0hero_class", "0");
  urlParams.set("edit_gegenstand_vorlage_0sockets", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0hero_race", "0");
  urlParams.set("edit_gegenstand_vorlage_0series", "");
  urlParams.set(
    "edit_gegenstand_vorlage_0not_usable_by_public_class_race_posted",
    "1"
  );
  urlParams.set("edit_gegenstand_vorlage_0calls_npc_id", "");
  urlParams.set("edit_gegenstand_vorlage_0series_protected", "");
  urlParams.set("edit_gegenstand_vorlage_0series_changed", "");
  urlParams.set("edit_gegenstand_vorlage_0skill_bonus", "0");
  urlParams.set("edit_gegenstand_vorlage_0skillclass_bonus", "0");
  urlParams.set("edit_gegenstand_vorlage_0public_only", "1");
  urlParams.set("edit_gegenstand_vorlage_0owner_id_SELECT", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0changer_id_SELECT", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0is_locked", "");
  urlParams.set("edit_gegenstand_vorlage_0is_editable", "");
  urlParams.set("edit_gegenstand_vorlage_0is_translated", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0is_public_foreign", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0auto_publish", "");
  urlParams.set("edit_gegenstand_vorlage_0genus", "");
  urlParams.set("edit_gegenstand_vorlage_0notiz", "");
  urlParams.set("edit_gegenstand_vorlage_0item_deleted", "no");
  urlParams.set("edit_gegenstand_vorlage_0profile_id", "0");
  urlParams.set("edit_gegenstand_vorlage_0is_open", "1");

  const response = await fetch(
    location.origin +
      `/wod/module/search/search_item.php?session_hero_id=${urlParams.get(
        "session_hero_id"
      )}&is_popup=1`,
    {
      headers: {
        accept:
          "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
        "content-type": "application/x-www-form-urlencoded",
      },
      body: urlParams.toString(),
      method: "POST",
    }
  );
  const html = await response.text();
  const $item = $(html).find(
    'a[href^="/wod/admin/edit_item/gegenstand_vorlagen_editor.php"], a[href^="/wod/spiel/hero/item.php"]'
  );
  if (!$item.length) {
    return null;
  }
  try {
    params = new URLSearchParams($item.attr("href").split("?")[1]);
    let itemId = params.get("EDIT_ID");
    if (!itemId) {
      itemId = params.get("item_id");
    }
    return itemId;
  } catch (ex) {
    console.error(ex);
    return null;
  }
}

async function getItemById(params, itemId, count) {
  const urlParams = new URLSearchParams(params.toString());
  if (!itemId) return;
  urlParams.set("si_plain", "1");
  urlParams.set(
    "si_backlink",
    "/var/www/wod_master/wod/spiel/admin/heroitems.php"
  );
  urlParams.set("si_title", `添加物品至 ${urlParams.get("heldenname")} 的仓库`);
  urlParams.set("si_message", "");
  urlParams.set("si_button", "");
  urlParams.set("si_action", "添加");
  urlParams.set("si_checkbox", "add_item_id");
  urlParams.set("si_input", "add_item_count");
  urlParams.set("si_input_def", "1");
  urlParams.set("si_input_size", "");
  urlParams.set("si_input_numeric", "1");
  urlParams.set("si_input_numsigned", "");
  urlParams.set("si_user_posts", "id");
  urlParams.set("si_submit_button", "");
  urlParams.set("id", urlParams.get("session_hero_id"));
  urlParams.set("si_action_exec", "添加");
  urlParams.set(`add_item_count[${itemId}]`, count ? count : "1");
  urlParams.set(`add_item_id[${itemId}]`, "1");

  urlParams.set("edit_gegenstand_vorlage_0name", "不可能存在的物品");
  urlParams.set("search_options_which_vers", "4.00");
  urlParams.set("edit_gegenstand_vorlage_0gegenstand_klasse", "0");
  urlParams.set("edit_gegenstand_vorlage_0trage_typ", "");
  urlParams.set("edit_gegenstand_vorlage_0drop_klasse", "0");
  urlParams.set("edit_gegenstand_vorlage_0unique_type", "");
  urlParams.set("edit_gegenstand_vorlage_0drop_from", "");
  urlParams.set("edit_gegenstand_vorlage_0drop_to", "");
  urlParams.set("edit_gegenstand_vorlage_0set", "0");
  urlParams.set("edit_gegenstand_vorlage_0drop_haeufigkeit", "-1");
  urlParams.set("edit_gegenstand_vorlage_0usage_items", "");
  urlParams.set("edit_gegenstand_vorlage_0hero_class", "0");
  urlParams.set("edit_gegenstand_vorlage_0sockets", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0hero_race", "0");
  urlParams.set("edit_gegenstand_vorlage_0series", "");
  urlParams.set(
    "edit_gegenstand_vorlage_0not_usable_by_public_class_race_posted",
    "1"
  );
  urlParams.set("edit_gegenstand_vorlage_0calls_npc_id", "");
  urlParams.set("edit_gegenstand_vorlage_0series_protected", "");
  urlParams.set("edit_gegenstand_vorlage_0series_changed", "");
  urlParams.set("edit_gegenstand_vorlage_0skill_bonus", "0");
  urlParams.set("edit_gegenstand_vorlage_0skillclass_bonus", "0");
  urlParams.set("edit_gegenstand_vorlage_0public_only", "1");
  urlParams.set("edit_gegenstand_vorlage_0owner_id_SELECT", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0changer_id_SELECT", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0is_locked", "");
  urlParams.set("edit_gegenstand_vorlage_0is_editable", "");
  urlParams.set("edit_gegenstand_vorlage_0is_translated", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0is_public_foreign", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0auto_publish", "");
  urlParams.set("edit_gegenstand_vorlage_0genus", "");
  urlParams.set("edit_gegenstand_vorlage_0notiz", "");
  urlParams.set("edit_gegenstand_vorlage_0item_deleted", "no");
  urlParams.set("edit_gegenstand_vorlage_0profile_id", "0");
  urlParams.set("edit_gegenstand_vorlage_0is_open", "1");
  urlParams.set("IS_POPUP", "1");

  const response = await fetch(
    location.origin +
      `/wod/module/search/search_item.php?session_hero_id=${urlParams.get(
        "session_hero_id"
      )}&is_popup=1`,
    {
      headers: {
        accept:
          "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
        "accept-language": "zh-CN,zh;q=0.9",
        "content-type": "application/x-www-form-urlencoded",
      },
      body: urlParams.toString(),
      method: "POST",
    }
  );
  return response;
}

async function getItemByName(urlParams, itemName, ver, count) {
  const itemId = await searchItemIdByNameAndVer(urlParams, itemName, ver);
  globalCurSteps++;
  showItemFetchProcess();
  try {
    if (!itemId) return;
    return await getItemById(urlParams, itemId, count);
  } finally {
    globalCurSteps2++;
    showItemFetchProcess();
  }
}

function showItemFetchProcess() {
  ajaxAlert(
    `6. 开始获取装备<br>物品ID查询进度：${globalCurSteps}/${globalSteps}<br>物品获取进度：${globalCurSteps2}/${globalSteps}`
  );
}

function reverseRacePro() {
  // 只在物品编辑页面生效
  const urls = ["/wod/admin/edit_item/gegenstand_vorlagen_editor.php"];
  if (!urls.includes(location.pathname)) {
    return;
  }
  // 物品编辑页面添加反选职业反选种族按钮
  const $reverseProBtn = $(
    '<button class="button clickable" type="button">反选职业</button>'
  );
  const $reverseRaceBtn = $(
    '<button class="button clickable" type="button">反选种族</button>'
  );
  const $table = $(
    'select[name^="EDIT_REQS_"][name$="class_restriction_type]"]+table'
  );
  $table.find("td:eq(1)").append($reverseProBtn);
  $table.find("td:eq(3)").append($reverseRaceBtn);
  $reverseProBtn.click(function () {
    $table.find("td:eq(0) :checkbox,td:eq(1) :checkbox").each(function () {
      this.checked = !this.checked;
    });
  });
  $reverseRaceBtn.click(function () {
    $table.find("td:eq(3) :checkbox").each(function () {
      this.checked = !this.checked;
    });
  });
}

/**
 *模拟form表单，实现post提交，并打开新窗口
 * url：请求链接
 * postData：json格式的post参数
 */
function formpost(url, postData) {
  var tempform = document.createElement("form");
  tempform.action = url;
  tempform.target = "_blank";
  tempform.method = "post";
  tempform.style.display = "none";
  for (var x in postData) {
    var opt = document.createElement("textarea");
    opt.name = x;
    opt.value = postData[x];
    tempform.appendChild(opt);
  }
  document.body.appendChild(tempform);
  tempform.submit().remove();
}

function gotoSearchItemsPage() {
  const urlParams = getBaseSearchParams();
  const url =
    location.origin +
    "/wod/module/search/search_item.php?is_popup=1&session_hero_id=" +
    urlParams.get("session_hero_id");
  urlParams.set("SPRACHE", "cn");
  urlParams.set("posted_lang", "cn");
  urlParams.set("SAVE_TRANS_LANG", "");
  urlParams.set("SI_TABLE_SORT_DIR", "ASC");
  urlParams.set("SI_TABLE_SORT_COL", "1");
  urlParams.set("SI_TABLE_PAGE", "1");
  urlParams.set("edit_gegenstand_vorlage_0name", "请输入物品名称");
  urlParams.set(
    "profile_data_edit_gegenstand_vorlage_0_profile_data",
    "HogNB0ny8I/FjaOg6FXrzZvwI0A1hZgI77jjRYoCRE3P2LwgQj0JR8bySr/7mEmrsSlv3GXCWD0RRuON2Ume6uHAHtHkbJmCzOd/WJ+rLrIfw/OsmiZucM4ZX+fVLEQV"
  );
  urlParams.set(
    "callback_js_code_edit_gegenstand_vorlage_0_callback_js_code",
    "0EIlGU9U2i1TDQ0a7Ui5ZQLoeWX0HucsaYkZcaw1SZjqC8fuJJQez5Tmhqp86XpVYEabDKvVsgGOFwBIvq4sn62Y1XmVgc08RH9odBsZi2tCtYAsEAbshLGIpXuyPN8PfUTrPOiXJhub6SZtcKEjWeovNQzKlVuhDVdTXMXZEu9/3U0JOk7eDG+PLHbjpgYR4mmAqVkPwOCmEsFL1o4AKw=="
  );
  urlParams.set("edit_gegenstand_vorlage_0gegenstand_klasse", "0");
  urlParams.set("edit_gegenstand_vorlage_0trage_typ", "");
  urlParams.set("edit_gegenstand_vorlage_0drop_klasse", "0");
  urlParams.set("edit_gegenstand_vorlage_0unique_type", "");
  urlParams.set("edit_gegenstand_vorlage_0drop_from", "");
  urlParams.set("edit_gegenstand_vorlage_0drop_to", "");
  urlParams.set("edit_gegenstand_vorlage_0set", "0");
  urlParams.set("edit_gegenstand_vorlage_0drop_haeufigkeit", "-1");
  urlParams.set("edit_gegenstand_vorlage_0usage_items", "");
  urlParams.set("edit_gegenstand_vorlage_0hero_class", "0");
  urlParams.set("edit_gegenstand_vorlage_0sockets", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0hero_race", "0");
  urlParams.set("edit_gegenstand_vorlage_0series", "");
  urlParams.set(
    "edit_gegenstand_vorlage_0not_usable_by_public_class_race_posted",
    "1"
  );
  urlParams.set("edit_gegenstand_vorlage_0calls_npc_id", "");
  urlParams.set("edit_gegenstand_vorlage_0series_protected", "");
  urlParams.set("edit_gegenstand_vorlage_0series_changed", "");
  urlParams.set("edit_gegenstand_vorlage_0skill_bonus", "0");
  urlParams.set("edit_gegenstand_vorlage_0skillclass_bonus", "0");
  urlParams.set("edit_gegenstand_vorlage_0public_only", "1");
  urlParams.set("search_options_which_vers", "4.00");
  urlParams.set("edit_gegenstand_vorlage_0owner_id_SELECT", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0changer_id_SELECT", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0is_locked", "");
  urlParams.set("edit_gegenstand_vorlage_0is_editable", "");
  urlParams.set("edit_gegenstand_vorlage_0is_translated", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0is_public_foreign", "NULL");
  urlParams.set("edit_gegenstand_vorlage_0auto_publish", "");
  urlParams.set("edit_gegenstand_vorlage_0genus", "");
  urlParams.set("edit_gegenstand_vorlage_0notiz", "");
  urlParams.set("edit_gegenstand_vorlage_0item_deleted", "no");
  urlParams.set("edit_gegenstand_vorlage_0profile_id", "0");
  urlParams.set("edit_gegenstand_vorlage_0is_open", "1");
  urlParams.set("si_plain", "1");
  urlParams.set(
    "si_backlink",
    "/var/www/wod_master/wod/spiel/admin/heroitems.php"
  );
  urlParams.set(
    "si_title",
    "添加物品至" + urlParams.get("heldenname") + "的仓库"
  );
  urlParams.set("si_message", "");
  urlParams.set("si_button", "");
  urlParams.set("si_action", "添加");
  urlParams.set("si_checkbox", "add_item_id");
  urlParams.set("si_input", "add_item_count");
  urlParams.set("si_input_def", "1");
  urlParams.set("si_input_size", "");
  urlParams.set("si_input_numeric", "1");
  urlParams.set("si_input_numsigned", "");
  urlParams.set("si_user_posts", "id");
  urlParams.set("si_submit_button", "");
  urlParams.set("id", urlParams.get("session_hero_id"));
  formpost(url, Object.fromEntries(urlParams));
}

async function fetchItems(itemList) {
  const urlParams = getBaseSearchParams();
  itemList = itemList.filter((item) => item.name);
  globalSteps = itemList.length;
  if (globalSteps == 0) {
    ajaxAlert("未输入物品名称！");
    return;
  }
  globalCurSteps = 0;
  globalCurSteps2 = 0;
  showItemFetchProcess();
  const promiseArr = [];
  for (let item of itemList) {
    promiseArr.push(getItemByName(urlParams, item.name, "4.00", item.count));
  }
  const responseArr = await Promise.all(promiseArr);
  ajaxAlert("处理完毕！");
}

function easyFetchItems() {
  const $groupDiv = $(".gadget.groupmsg.lang-cn");
  if (!$groupDiv.length) return;
  const $container = $(
    '<div id="easyFetchItemsDiv" style="display: flex;flex-direction: column;"></div>'
  );
  const $textArea = $(
    '<textarea id="easyFetchItemsInput" rows="4" placeholder="输入需要的物品，一行一个，例如 新鲜的蜘蛛草*3 或者 毒石匕首"></textarea>'
  );
  const $fetchBtn = $(
    '<button name="easyFetchItemsBtn" class="button clickable">获取指定物品</button>'
  );
  const $manualBtn = $(
    '<button name="manualFetchItemsBtn" class="button clickable">手动获取物品</button>'
  );
  $container.append($textArea, $fetchBtn, $manualBtn);
  $groupDiv.before($container);

  $manualBtn.click(function () {
    gotoSearchItemsPage();
  });

  $fetchBtn.click(async function () {
    let itemList = [];
    let text = $textArea.val();
    for (const t of text.split("\n")) {
      const s = t.split("*");
      itemList.push({ name: s[0], count: s[1] || 1 });
    }
    await fetchItems(itemList);
  });
}